#!/bin/bash

###################
# 常量定义
###################
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly NC='\033[0m'

readonly REQUIRED_PORTS=(5000 3001 3002)
readonly REQUIRED_COMMANDS=("git" "npm" "node" "curl" "wget" "tar")
readonly NODE_VERSION="18"

# 支持的操作系统
readonly SUPPORTED_OS=("rhel" "centos" "rocky" "almalinux")

# 包管理器命令
PKG_MANAGER=""
PKG_UPDATE="" 
PKG_INSTALL=""

###################
# 工具函数
###################
log_info() { echo -e "${GREEN}[INFO] $1${NC}"; }
log_warn() { echo -e "${YELLOW}[WARN] $1${NC}"; }
log_error() { echo -e "${RED}[ERROR] $1${NC}"; }

# 检查磁盘空间
check_disk_space() {
    local dir=$1
    # 如果目录不存在，检查父目录
    while [ ! -d "$dir" ] && [ "$dir" != "/" ]; do
        dir=$(dirname "$dir")
    done

    # 如果到达根目录仍不存在，使用根目录
    if [ "$dir" = "/" ]; then
        dir="/"
    fi

    # 获取可用空间（MB）
    local free_space
    free_space=$(df -m "$dir" | awk 'NR==2 {print $4}')

    # 检查df命令是否成功
    if [ $? -ne 0 ] || [ -z "$free_space" ]; then
        log_error "无法获取磁盘空间信息"
        exit 1
    fi

    # 检查是否是数字
    if ! [[ "$free_space" =~ ^[0-9]+$ ]]; then
        log_error "获取到的磁盘空间值无效: $free_space"
        exit 1
    fi

    # 检查空间是否足够
    if [ "$free_space" -lt 1024 ]; then
        log_error "磁盘空间不足，需要至少1GB可用空间"
        log_error "当前可用空间: ${free_space}MB"
        log_error "检查目录: $dir"
        exit 1
    fi

    log_info "磁盘空间检查通过：${free_space}MB可用"
}

# 检测Linux发行版
detect_os() {
    local os_name=""
    
    # 检查常见的发行版标识文件
    if [ -f "/etc/redhat-release" ]; then
        os_name=$(cat /etc/redhat-release | tr '[:upper:]' '[:lower:]')
        if [[ $os_name == *"red hat"* ]]; then
            echo "rhel"
        elif [[ $os_name == *"centos"* ]]; then
            echo "centos"
        elif [[ $os_name == *"rocky"* ]]; then
            echo "rocky"
        elif [[ $os_name == *"almalinux"* ]]; then
            echo "almalinux"
        fi
    else
        echo "unknown"
    fi
}

# 设置包管理器
setup_package_manager() {
    local os_type=$(detect_os)
    
    case $os_type in
        "rhel"|"centos"|"rocky"|"almalinux")
            if command -v dnf >/dev/null 2>&1; then
                PKG_MANAGER="dnf"
            else
                PKG_MANAGER="yum"
            fi
            PKG_UPDATE="$PKG_MANAGER update -y"
            PKG_INSTALL="$PKG_MANAGER install -y"
            ;;
        *)
            log_error "不支持的操作系统"
            exit 1
            ;;
    esac
}

# 错误处理
handle_error() {
    local exit_code=$?
    local command=$1
    
    if [[ $command == *"git clone"* ]]; then
        return
    fi
    
    log_error "命令执行失败: $command"
    log_error "错误代码: $exit_code"
    cleanup_and_exit $exit_code
}

# 清理并退出
cleanup_and_exit() {
    local exit_code=${1:-1}
    if [ -d "$INSTALL_DIR" ]; then
        log_warn "清理安装目录..."
        rm -rf "$INSTALL_DIR"
    fi
    exit $exit_code
}

###################
# 系统检查函数
###################
check_system_requirements() {
    log_info "检查系统要求..."
    
    # 检查是否为root用户
    if [[ $EUID -ne 0 ]]; then
        log_error "此脚本必须以root用户运行"
        exit 1
    fi
    
    # 检查操作系统
    local os_type=$(detect_os)
    if [[ ! " ${SUPPORTED_OS[@]} " =~ " ${os_type} " ]]; then
        log_error "此脚本仅支持Red Hat系列操作系统"
        log_error "支持的系统: ${SUPPORTED_OS[*]}"
        log_error "当前系统: $os_type"
        exit 1
    fi
    
    # 设置包管理器
    setup_package_manager
    
    # 检查内存
    local total_mem=$(free -m | awk '/^Mem:/{print $2}')
    if [ $total_mem -lt 2048 ]; then
        log_error "系统内存不足，需要至少2GB内存"
        exit 1
    fi
    
    # 检查磁盘空间
    check_disk_space "$INSTALL_DIR"
}

# 检查并安装必要命令
install_required_commands() {
    log_info "检查必要命令..."
    local missing_commands=()
    
    # 先移除node和npm从必需命令列表中
    local base_commands=()
    for cmd in "${REQUIRED_COMMANDS[@]}"; do
        if [[ "$cmd" != "node" && "$cmd" != "npm" ]]; then
            base_commands+=("$cmd")
        fi
    done
    
    for cmd in "${base_commands[@]}"; do
        if ! command -v "$cmd" >/dev/null 2>&1; then
            missing_commands+=("$cmd")
        fi
    done
    
    if [ ${#missing_commands[@]} -ne 0 ]; then
        log_info "安装缺失的命令: ${missing_commands[*]}"
        $PKG_INSTALL "${missing_commands[@]}"
    fi
}

# 检查端口占用
check_ports() {
    log_info "检查端口占用..."
    for port in "${REQUIRED_PORTS[@]}"; do
        if netstat -tuln | grep -q ":$port "; then
            log_error "端口 $port 已被占用"
            exit 1
        fi
    done
}

###################
# 安装函数
###################
install_node() {
    log_info "安装Node.js..."
    
    # 检查是否已安装Node.js
    if ! command -v node &> /dev/null; then
        log_info "配置Node.js仓库..."
        
        # 清理可能存在的旧配置
        rm -f /etc/yum.repos.d/nodesource*
        
        # 添加NodeSource仓库
        curl -fsSL "https://rpm.nodesource.com/setup_${NODE_VERSION}.x" | bash -
        
        if [ $? -ne 0 ]; then
            log_error "Node.js仓库配置失败"
            exit 1
        fi
        
        log_info "安装Node.js包..."
        $PKG_INSTALL nodejs
        
        if [ $? -ne 0 ]; then
            log_error "Node.js安装失败"
            exit 1
        fi
    fi
    
    # 验证安装
    if ! command -v node &> /dev/null; then
        log_error "Node.js安装验证失败"
        exit 1
    fi
    
    # 验证Node.js版本
    local node_version=$(node -v | cut -d'v' -f2)
    if [[ "${node_version%%.*}" -lt $NODE_VERSION ]]; then
        log_error "Node.js版本过低，需要v${NODE_VERSION}或更高版本"
        log_error "当前版本: v${node_version}"
        exit 1
    fi
    
    log_info "Node.js v${node_version} 安装成功"
    
    # 验证npm
    if ! command -v npm &> /dev/null; then
        log_error "npm未正确安装"
        exit 1
    fi
    
    local npm_version=$(npm -v)
    log_info "npm v${npm_version} 可用"
}

configure_npm() {
    log_info "配置npm..."
    
    # 清除npm缓存
    npm cache clean --force
    
    # 配置npm镜像
    npm config set registry https://registry.npmmirror.com
    
    # 验证npm配置
    local current_registry=$(npm config get registry)
    current_registry=${current_registry%/}
    if [[ "$current_registry" != "https://registry.npmmirror.com" ]]; then
        log_error "npm镜像配置失败"
        log_error "当前镜像: $current_registry"
        exit 1
    fi
    
    # 配置其他npm选项
    npm config set legacy-peer-deps true
    npm config set fund false
    npm config set audit false
    npm config set update-notifier false
    
    # 配置全局安装路径
    mkdir -p /usr/local/{lib/node_modules,bin}
    chown -R $SUDO_USER:$SUDO_USER /usr/local/{lib/node_modules,bin}
    npm config set prefix '/usr/local'
    
    # 验证全局路径配置
    local npm_prefix=$(npm config get prefix)
    if [[ "$npm_prefix" != "/usr/local" ]]; then
        log_warn "npm全局路径配置可能有问题"
        log_warn "当前全局路径: $npm_prefix"
        log_warn "尝试修复..."
        
        # 尝试使用替代方法设置
        su - $SUDO_USER -c "
            npm config set prefix '/usr/local'
            npm config set unsafe-perm true
        "
    fi
    
    log_info "npm配置完成"
    
    # 显示npm配置信息
    log_info "npm当前配置："
    log_info "- 镜像: $(npm config get registry)"
    log_info "- 全局路径: $(npm config get prefix)"
}

install_global_dependencies() {
    log_info "安装全局依赖..."
    
    # 设置npm安装选项
    local npm_install_opts="--force --no-fund --no-audit"
    
    # 安装PM2
    if ! npm install -g pm2 $npm_install_opts; then
        log_error "PM2安装失败"
        exit 1
    fi
    
    # 验证PM2安装
    export PATH="$PATH:/usr/local/bin"
    if ! which pm2 >/dev/null 2>&1; then
        log_error "PM2未正确安装"
        log_error "PATH: $PATH"
        log_error "PM2路径: $(which pm2 2>/dev/null || echo '未找到')"
        exit 1
    fi
    
    log_info "PM2安装成功: $(pm2 --version)"
}

###################
# 部署函数
###################
deploy_application() {
    # 克隆代码
    clone_repository
    
    # 安装依赖
    install_dependencies
    
    # 创建配置文件
    create_configurations
    
    # 构建前端
    build_frontend
    
    # 配置服务
    configure_services
    
    # 配置备份
    configure_backup
}

###################
# 配置函数
###################
get_user_configuration() {
    log_info "配置部署参数..."
    
    # 获取服务器IP
    SERVER_IP=$(ip addr | grep 'inet ' | grep -v '127.0.0.1' | head -n1 | awk '{print $2}' | cut -d/ -f1)
    echo "检测到服务器IP地址: ${SERVER_IP}"
    read -p "是否使用此IP地址? (y/n): " use_detected_ip
    
    if [[ $use_detected_ip != "y" ]]; then
        read -p "请输入服务器IP地址: " SERVER_IP
    fi
    
    # 设置默认值
    DEFAULT_INSTALL_DIR="/opt/games"
    DEFAULT_API_URL="http://${SERVER_IP}:5000"
    
    # 获取安装目录
    read -p "请输入安装目录 (默认: $DEFAULT_INSTALL_DIR): " INSTALL_DIR
    INSTALL_DIR=${INSTALL_DIR:-$DEFAULT_INSTALL_DIR}
    
    # 获取API地址
    read -p "请输入API地址 (默认: $DEFAULT_API_URL): " API_URL
    API_URL=${API_URL:-$DEFAULT_API_URL}
    
    # 确认配置
    echo -e "\n=== 部署配置确认 ==="
    echo "安装目录: $INSTALL_DIR"
    echo "API地址: $API_URL"
    echo "服务器IP: $SERVER_IP"
    
    read -p "确认以上配置正确吗? (y/n): " confirm
    if [[ $confirm != "y" ]]; then
        log_error "已取消部署"
        exit 1
    fi
}

clone_repository() {
    log_info "克隆项目代码..."
    
    local repo_url="https://gitee.com/ruan-yuguang/linux_game.git"
    local target_dir="$INSTALL_DIR"
    
    # 创建所有必要的目录
    mkdir -p "$target_dir"/{server,shared,shudu/client,shuzhi/client,logs,backup}
    chown -R $SUDO_USER:$SUDO_USER "$target_dir"
    
    # 确保目录为空
    if [ -d "$target_dir" ]; then
        if [ "$(ls -A $target_dir)" ]; then
            log_warn "目标目录不为空，清理中..."
            rm -rf "${target_dir:?}"/*
        fi
    fi
    
    local max_retries=3
    local retry_count=0
    local success=false
    
    while [ $retry_count -lt $max_retries ] && [ "$success" != true ]; do
        log_info "尝试克隆代码 (尝试 $((retry_count + 1))/$max_retries)..."
        if su - $SUDO_USER -c "
            git clone --progress $repo_url $target_dir 2>&1 || exit 1
        "; then
            success=true
            log_info "代码克隆成功"
        else
            retry_count=$((retry_count + 1))
            if [ $retry_count -lt $max_retries ]; then
                log_warn "克隆失败，等待${retry_count}秒后重试..."
                sleep $retry_count
            else
                log_error "克隆失败，已达到最大重试次数"
            fi
        fi
    done
    
    if [ "$success" != true ]; then
        log_error "克隆代码失败"
        log_error "请检查："
        log_error "1. 网络连接是否正常"
        log_error "2. 仓库地址是否正确：$repo_url"
        log_error "3. 目标目录权限是否正确：$target_dir"
        cleanup_and_exit 1
    fi
    
    # 验证克隆结果
    local required_files=(
        "ecosystem.config.js"
        "server/package.json"
        "shared/package.json"
        "shudu/client/package.json"
        "shuzhi/client/package.json"
    )
    
    local missing_files=()
    for file in "${required_files[@]}"; do
        if [ ! -f "$target_dir/$file" ]; then
            missing_files+=("$file")
        fi
    done
    
    if [ ${#missing_files[@]} -ne 0 ]; then
        log_error "克隆似乎成功但缺少关键文件"
        log_error "缺少的文件: ${missing_files[*]}"
        cleanup_and_exit 1
    fi
}

install_dependencies() {
    log_info "安装项目依赖..."
    
    local projects=("shared" "server" "shudu/client" "shuzhi/client")
    local npm_install_opts="--legacy-peer-deps --no-fund --no-audit"
    
    # 清理node_modules和package-lock.json
    for project in "${projects[@]}"; do
        log_info "清理 $project 旧依赖..."
        su - $SUDO_USER -c "
            cd $INSTALL_DIR/$project
            rm -rf node_modules package-lock.json
        "
    done
    
    # 安装特定版本的关键依赖
    for project in "shudu/client" "shuzhi/client"; do
        log_info "安装 $project 核心依赖..."
        su - $SUDO_USER -c "
            cd $INSTALL_DIR/$project
            npm install ajv@^6.12.6 ajv-keywords@^3.5.2 $npm_install_opts
        "
    done
    
    for project in "${projects[@]}"; do
        log_info "安装 $project 依赖..."
        if ! su - $SUDO_USER -c "cd $INSTALL_DIR/$project && npm install $npm_install_opts"; then
            log_error "$project 依赖安装失败"
            cleanup_and_exit 1
        fi
    done
}

create_configurations() {
    log_info "创建配置文件..."
    
    # 创建共享环境配置
    su - $SUDO_USER -c "cat > $INSTALL_DIR/shared/.env.production << EOF
NODE_ENV=production
API_URL=$API_URL
EOF"

    # 创建后端环境配置
    su - $SUDO_USER -c "cat > $INSTALL_DIR/server/.env << EOF
PORT=5000
HOST=0.0.0.0
NODE_ENV=production
EOF"

    # 创建前端环境配置
    for project in "shudu" "shuzhi"; do
        su - $SUDO_USER -c "cat > $INSTALL_DIR/$project/client/.env.production << EOF
REACT_APP_API_URL=$API_URL
EOF"
    done
}

build_frontend() {
    log_info "构建前端项目..."
    
    # 设置构建环境变量
    export NODE_OPTIONS="--max-old-space-size=4096"
    
    for project in "shudu" "shuzhi"; do
        log_info "构建 $project 前端..."
        if ! su - $SUDO_USER -c "
            cd $INSTALL_DIR/$project/client
            export NODE_OPTIONS='--max-old-space-size=4096'
            npm run build
        "; then
            log_error "$project 前端构建失败"
            log_error "请检查构建日志:"
            su - $SUDO_USER -c "
                cat $INSTALL_DIR/$project/client/npm-debug.log 2>/dev/null || true
            "
            log_error "$project 前端构建失败"
            cleanup_and_exit 1
        fi
    done
    
    # 验证构建结果
    for project in "shudu" "shuzhi"; do
        if [ ! -d "$INSTALL_DIR/$project/client/build" ]; then
            log_error "$project 前端构建输出目录不存在"
            cleanup_and_exit 1
        fi
        
        if [ ! -f "$INSTALL_DIR/$project/client/build/index.html" ]; then
            log_error "$project 前端构建似乎不完整"
            cleanup_and_exit 1
        fi
    done
}

configure_services() {
    log_info "配置服务..."
    
    # 确保PM2目录存在
    su - $SUDO_USER -c "
        mkdir -p ~/.pm2
    "
    
    # 更新PM2配置文件中的路径
    su - $SUDO_USER -c "
        sed -i 's|/opt/games|$INSTALL_DIR|g' $INSTALL_DIR/ecosystem.config.js
    "
    
    # 先移除可能存在的PM2启动配置
    su - $SUDO_USER -c "
        pm2 unstartup systemd 2>/dev/null || true
    "
    
    # 启动服务
    su - $SUDO_USER -c "
        cd $INSTALL_DIR
        pm2 start ecosystem.config.js
        pm2 save
    "
    
    # 配置PM2开机自启
    su - $SUDO_USER -c "
        pm2 startup systemd -u $SUDO_USER --hp /home/$SUDO_USER
        sudo env PATH=$PATH:/usr/local/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u $SUDO_USER --hp /home/$SUDO_USER
        pm2 save
    "
    
    # 配置防火墙
    for port in "${REQUIRED_PORTS[@]}"; do
        firewall-cmd --permanent --add-port=$port/tcp
    done
    firewall-cmd --reload
}

configure_backup() {
    log_info "配置备份..."
    
    # 确保备份目录存在
    mkdir -p "$INSTALL_DIR/backup"
    chown $SUDO_USER:$SUDO_USER "$INSTALL_DIR/backup"
    
    # 创建备份脚本
    su - $SUDO_USER -c "cat > $INSTALL_DIR/backup/backup.sh << EOF
#!/bin/bash
BACKUP_DIR=\"$INSTALL_DIR/backup\"
DATE=\\\$(date +%Y%m%d)
KEEP_DAYS=7

# 创建备份
tar -czf \\\$BACKUP_DIR/games-\\\$DATE.tar.gz \\\\
    $INSTALL_DIR/server \\\\
    $INSTALL_DIR/shared \\\\
    $INSTALL_DIR/*/client/build \\\\
    $INSTALL_DIR/ecosystem.config.js

# 删除旧备份
find \\\$BACKUP_DIR -name \"games-*.tar.gz\" -mtime +\\\$KEEP_DAYS -delete
EOF"
    
    # 添加执行权限
    su - $SUDO_USER -c "chmod +x $INSTALL_DIR/backup/backup.sh"
    
    # 添加定时任务
    su - $SUDO_USER -c "(crontab -l 2>/dev/null; echo \"0 2 * * * $INSTALL_DIR/backup/backup.sh\") | crontab -"
    
    # 验证备份脚本
    if [ ! -x "$INSTALL_DIR/backup/backup.sh" ]; then
        log_error "备份脚本创建失败或权限不正确"
        cleanup_and_exit 1
    fi
}

verify_deployment() {
    log_info "验证部署..."
    sleep 5 # 等待服务启动
    
    # 验证服务状态
    local services=("games-server" "sudoku-frontend" "nonogram-frontend")
    local failed_services=()
    
    for service in "${services[@]}"; do
        if ! su - $SUDO_USER -c "pm2 show $service" >/dev/null 2>&1; then
            failed_services+=("$service")
        fi
    done
    
    if [ ${#failed_services[@]} -ne 0 ]; then
        log_error "以下服务未正常运行: ${failed_services[*]}"
        log_info "请检查日志文件获取详细信息"
        cleanup_and_exit 1
    fi
}

show_deployment_results() {
    echo -e "\n${GREEN}=== 部署完成 ===${NC}"
    echo "您可以通过以下地址访问服务："
    echo "1. 后端API: $API_URL/health"
    echo "2. 数独游戏: http://$SERVER_IP:3001"
    echo "3. 数织游戏: http://$SERVER_IP:3002"
    
    echo -e "\n常用维护命令："
    echo "- 查看服务状态: pm2 status"
    echo "- 查看日志: pm2 logs"
    echo "- 重启服务: pm2 restart all"
    echo "- 停止服务: pm2 stop all"
    
    echo -e "\n如果遇到问题，请查看日志文件："
    echo "- 日志目录: $INSTALL_DIR/logs/"
    echo "- 备份目录: $INSTALL_DIR/backup/"
    
    echo -e "\n${YELLOW}注意：请确保防火墙已允许访问端口 3001, 3002, 5000${NC}"
}

###################
# 主函数
###################
main() {
    echo -e "${GREEN}=== 游戏平台部署脚本 ===${NC}"
    echo "作者: ruan-yuguang"
    
    # 0. 检测系统类型并设置包管理器
    setup_package_manager
    log_info "使用包管理器: $PKG_MANAGER"
    
    # 1. 获取配置
    get_user_configuration
    
    # 2. 系统检查
    check_system_requirements
    check_ports
    install_required_commands
    
    # 3. 环境准备
    install_node
    configure_npm
    install_global_dependencies
    
    # 4. 应用部署
    deploy_application
    
    # 5. 验证部署
    verify_deployment
    
    # 6. 显示结果
    show_deployment_results
}

# 设置错误处理
set -e
trap 'handle_error "$BASH_COMMAND"' ERR

# 执行主函数
main "$@" 